package com.jeebey.mybatis.utils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * @since 1.0
 * @author <a href="mailto:84961426@qq.com">JuST4iT</a>
 * @version $Id: SqlExecutorUtil.java Feb 18, 2016 6:07:02 PM $
 */

public class SqlExecutorUtil {

	protected static Log log = LogFactory.getLog(SqlExecutorUtil.class);

	public static String removeBreakingWhitespace(String original) {
		StringTokenizer whitespaceStripper = new StringTokenizer(original);
		StringBuilder builder = new StringBuilder();
		for (; whitespaceStripper.hasMoreTokens(); builder.append(" ")){
			builder.append(whitespaceStripper.nextToken());
		}
		return builder.toString();
	}

	public static long getCount(final Connection connection, final MappedStatement mappedStatement,
			final Object parameterObject, final String countSql, List<ParameterMapping> parameterMappings)
			throws SQLException {
		if (log.isDebugEnabled()) {
			log.debug("==>  Preparing: " + removeBreakingWhitespace(countSql));
		}

		Connection _connection = connection;
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		boolean isCreateConn = (connection == null); // 是否需要从连接池中获取新连接
		try {
			if (isCreateConn) {
				_connection = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection();
			}
			preparedStatement = _connection.prepareStatement(countSql);
			BoundSql boundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings,
					parameterObject);
			setParameters(preparedStatement, mappedStatement, boundSql, parameterObject);
			resultSet = preparedStatement.executeQuery();
			int iCount = resultSet.next() ? resultSet.getInt(1) : 0;
			if (log.isDebugEnabled()) {
				log.debug("<==      Total: 1 Count: " + iCount);
			}
			return iCount;
		} finally {
			if (resultSet != null) {
				resultSet.close();
			}
			if (preparedStatement != null) {
				preparedStatement.close();
			}
			if (isCreateConn) {
				_connection.close();
			}
		}
	}

	@SuppressWarnings("unchecked")
	public static void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
			Object parameterObject) throws SQLException {
		ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
		List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		if (parameterMappings != null) {
			Configuration configuration = mappedStatement.getConfiguration();
			TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
			MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
			StringBuilder parameterStrBuilder = new StringBuilder("==> Parameters: ");
			for (int i = 0; i < parameterMappings.size(); i++) {
				ParameterMapping parameterMapping = parameterMappings.get(i);
				if (parameterMapping.getMode() != ParameterMode.OUT) {
					Object value = null;
					String propertyName = parameterMapping.getProperty();
					PropertyTokenizer prop = new PropertyTokenizer(propertyName);
					if (parameterObject == null) {
						value = null;
					} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
						value = parameterObject;
					} else if (boundSql.hasAdditionalParameter(propertyName)) {
						value = boundSql.getAdditionalParameter(propertyName);
					} else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)
							&& boundSql.hasAdditionalParameter(prop.getName())) {
						value = boundSql.getAdditionalParameter(prop.getName());
						if (value != null) {
							value = configuration.newMetaObject(value).getValue(
									propertyName.substring(prop.getName().length()));
						}
					} else {
						value = metaObject == null ? null : metaObject.getValue(propertyName);
					}
					@SuppressWarnings("rawtypes")
					TypeHandler typeHandler = parameterMapping.getTypeHandler();
					if (typeHandler == null) {
						throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName
								+ " of statement " + mappedStatement.getId());
					}
					parameterStrBuilder.append(value).append("(").append(value.getClass().getSimpleName()).append("),");
					typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
				}
			}
			if (parameterStrBuilder.length() > 0) {
				parameterStrBuilder.deleteCharAt(parameterStrBuilder.length() - 1);
			}
			if (log.isDebugEnabled()) {
				log.debug(parameterStrBuilder.toString());
			}

		}
	}
}
